% partb.pl
% Inverting a Tower
% Four operators for the blocks domain

:- add_rule(
pick_up
  if task(pick_up(X)) and
     block(X) and table(Y) and on(X, Y) and
     hand(Z) and not(holding(Z, _))
  then remove(task(pick_up(X))) and
       remove(on(X, Y)) and add(holding(Z, X))).

:- add_rule(
unstack
  if task(unstack(X, Y)) and
     block(X) and block(Y) and hand(Z) and
     on(X, Y) and not(on(_, X)) and not(holding(Z, _))
  then remove(task(unstack(X, Y))) and
       remove(on(X, Y)) and add(holding(Z, X))).

:- add_rule(
put_down
  if task(put_down(X)) and
     block(X) and table(Y) and hand(Z) and
     holding(Z, X)
  then remove(task(put_down(X))) and
       remove(holding(Z, X)) and add(on(X, Y))).

:- add_rule(
stack
  if task(stack(X, Y)) and
     block(X) and block(Y) and hand(Z) and
     holding(Z, X) and not(on(_, Y))
  then remove(task(stack(X, Y))) and
       remove(holding(Z, X)) and add(on(X, Y))).

%%% three intermediate rules

:- add_rule(
unstack_and_put_down
  if task(unstack_and_put_down(X, Y)) and
     block(X) and block(Y)
  then remove(task(unstack_and_put_down(X, Y))) and
       add(task(unstack(X, Y))) and
       add(task(put_down(X)))
).

:- add_rule(
unstack_and_stack
  if task(unstack_and_stack(X, Y, Z)) and
     block(X) and block(Y) and block(Z)
  then remove(task(unstack_and_stack(X, Y, Z))) and
       add(task(unstack(X, Y))) and
       add(task(stack(X, Z)))
).

:- add_rule(
pick_up_and_stack
  if task(pick_up_and_stack(X, Y)) and
     block(X) and block(Y) and table(T) and on(X, T)
  then remove(task(pick_up_and_stack(X, Y))) and
       add(task(pick_up(X))) and
       add(task(stack(X, Y)))
).

%%% three high-level production rules

:- add_rule(
invert_tower1
  if task(invert_tower([X|Rest])) and
     block(X) and
     block(Y) and on(X, Y) and
     not(inverted_tower(_))
  then remove(task(invert_tower(_))) and
       add(task(unstack_and_put_down(X, Y))) and
       add(task(invert_tower(Rest))) and
       add(inverted_tower([X]))
).

:- add_rule(
invert_tower2
  if task(invert_tower([X|Rest])) and
     block(X) and
     block(Y) and on(X, Y) and
     block(Z) and not(on(_, Z)) and check(not_equal(X, Z)) and
     inverted_tower(Result)
  then remove(task(invert_tower(_))) and
       add(task(unstack_and_stack(X, Y, Z))) and
       add(task(invert_tower(Rest))) and
       remove(inverted_tower(_)) and
       add(inverted_tower([X|Result]))
).

:- add_rule(
invert_tower3
  if task(invert_tower([X])) and
     block(X) and table(T) and on(X, T) and
     block(Y) and not(on(_, Y)) and check(not_equal(X, Y)) and
     inverted_tower(Result)
  then remove(task(invert_tower(_))) and
       add(task(pick_up_and_stack(X, Y))) and
       add(task(invert_tower([]))) and
       remove(inverted_tower(_)) and
       add(inverted_tower([X|Result]))
).

%%% print end message

:- add_rule(
invert_done
  if task(invert_tower([])) and
     inverted_tower(Result)
  then remove(task(invert_tower(_))) and
       print('I have inverted the tower to produce ') and
       print(Result) and
       print('.\n') and
       end
).